package blockchain

import (
	"aqchain/pkg/blockchain/contract/item"
	"aqchain/pkg/log"
	"aqchain/pkg/model"
	"context"
	"gitee.com/aqchain/go-ethereum/accounts/abi/bind"
	"go.uber.org/zap"
	"reflect"
	"strings"
)

// SubscribeItemContractEvent 订阅确权合约事件
func (c *Client) SubscribeItemContractEvent(ctx context.Context) error {
	createChan := make(chan *item.ItemContractCreate)
	transferChan := make(chan *item.ItemContractTransfer)
	approvalChan := make(chan *item.ItemContractApproval)
	createSub, err := c.itemContract.WatchCreate(&bind.WatchOpts{Context: ctx}, createChan, nil, nil)
	if err != nil {
		return err
	}
	defer createSub.Unsubscribe()

	transferSub, err := c.itemContract.WatchTransfer(&bind.WatchOpts{Context: ctx}, transferChan, nil, nil, nil)
	if err != nil {
		return err
	}
	defer transferSub.Unsubscribe()

	approvalSub, err := c.itemContract.WatchApproval(&bind.WatchOpts{Context: ctx}, approvalChan, nil, nil, nil)
	if err != nil {
		return err
	}
	defer approvalSub.Unsubscribe()

	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case err = <-createSub.Err():
			return err
		case err = <-transferSub.Err():
			return err
		case err = <-approvalSub.Err():
			return err
		case event := <-createChan:
			err = c.handleCreateItem(event)
			if err != nil {
				return err
			}
		case event := <-transferChan:
			err = c.handleTransferItem(event)
			if err != nil {
				return err
			}
		case event := <-approvalChan:
			err = c.handleApprovalItem(event)
			if err != nil {
				return err
			}
		}
	}
}

func (c *Client) handleCreateItem(event *item.ItemContractCreate) error {
	block, err := c.client.BlockByHash(context.Background(), event.Raw.BlockHash)
	if err != nil {
		return err
	}
	itemRecord := model.Item{DCI: event.Dci}
	c.db.First(&itemRecord)
	// 创建Item
	itemRecord.DCI = event.Dci
	itemRecord.Name = event.Name
	itemRecord.TokenURI = event.TokenURI
	itemRecord.TokenID = event.TokenId.Uint64()
	itemRecord.Creator = model.Account{Address: event.From.String()}
	itemRecord.Owner = model.Account{Address: event.From.String()}
	// 创建ItemEvent
	newItemEvent := model.ItemEvent{
		TxHash:      event.Raw.TxHash.String(),
		DCI:         event.Dci,
		TokenID:     event.TokenId.Uint64(),
		From:        event.From.String(),
		EventName:   "create",
		BlockNumber: event.Raw.BlockNumber,
		BlockTime:   block.Time().Uint64(),
	}
	itemRecord.Create = newItemEvent
	itemRecord.Transfer = newItemEvent
	err = c.db.Save(&itemRecord).Error
	if err != nil {
		return err
	}
	log.Logger.Debug("接收到作品创建事件", zap.String("DCI", event.Dci), zap.String("Creator", event.From.String()))
	return nil
}

func (c *Client) handleTransferItem(event *item.ItemContractTransfer) error {
	block, err := c.client.BlockByHash(context.Background(), event.Raw.BlockHash)
	if err != nil {
		return err
	}
	// 根据DCI获取Item
	var itemRecord model.Item
	err = c.db.Where(&model.Item{DCI: event.Dci}).First(&itemRecord).Error
	if err != nil {
		return err
	}
	// 创建ItemEvent
	newItemEvent := model.ItemEvent{
		TxHash:      event.Raw.TxHash.String(),
		DCI:         event.Dci,
		TokenID:     event.TokenId.Uint64(),
		From:        event.From.String(),
		To:          event.To.String(),
		EventName:   "transfer",
		BlockNumber: event.Raw.BlockNumber,
		BlockTime:   block.Time().Uint64(),
	}
	err = c.db.Model(&newItemEvent).Create(&newItemEvent).Error
	if err != nil {
		return err
	}
	// 修改Item
	// 反射来遍历配置中的合约判断是否将作品转给了合约地址
	var flag bool
	contracts := reflect.ValueOf(c.config.Ethereum.Contracts)
	for i := 0; i < contracts.NumField(); i++ {
		if strings.ToLower(event.To.String()) == strings.ToLower(contracts.Field(i).String()) {
			flag = true
			break
		}
	}
	if flag {
		itemRecord.ContractAddress = event.To.String()
	} else {
		itemRecord.Owner = model.Account{Address: event.To.String()}
		itemRecord.ContractAddress = ""
	}
	itemRecord.ApprovalAddress = ""
	itemRecord.TransferTxHash = newItemEvent.TxHash
	itemRecord.Transfer = newItemEvent
	err = c.db.Model(&itemRecord).Select("OwnerAddress", "Owner", "ContractAddress", "ApprovalAddress", "TransferTxHash").Updates(&itemRecord).Error
	if err != nil {
		return err
	}
	log.Logger.Debug("接收到作品转移事件", zap.String("DCI", event.Dci), zap.String("Seller", event.From.String()), zap.String("Buyer", event.To.String()))
	return nil
}

func (c *Client) handleApprovalItem(event *item.ItemContractApproval) error {
	block, err := c.client.BlockByHash(context.Background(), event.Raw.BlockHash)
	if err != nil {
		return err
	}
	// 根据DCI获取Item
	var itemRecord model.Item
	err = c.db.Where(&model.Item{DCI: event.Dci}).First(&itemRecord).Error
	if err != nil {
		return err
	}
	// 修改Item
	itemRecord.ApprovalAddress = event.To.String()
	// 创建ItemEvent
	newItemEvent := model.ItemEvent{
		TxHash:      event.Raw.TxHash.String(),
		DCI:         event.Dci,
		TokenID:     event.TokenId.Uint64(),
		From:        event.From.String(),
		To:          event.To.String(),
		EventName:   "approval",
		BlockNumber: event.Raw.BlockNumber,
		BlockTime:   block.Time().Uint64(),
	}
	c.db.Create(&newItemEvent)
	err = c.db.Model(&itemRecord).Select("ApprovalAddress").Updates(&itemRecord).Error
	if err != nil {
		return err
	}
	log.Logger.Debug("接收到作品授权事件", zap.String("DCI", event.Dci), zap.String("Seller", event.From.String()), zap.String("Buyer", event.To.String()))
	return nil
}
